package com.ssy.lingxi.platform.template.service.serviceimpl.web;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.Projections;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import com.ssy.lingxi.common.constant.basic.UserLoginSourceEnum;
import com.ssy.lingxi.common.constant.manage.ShopEnvironmentEnum;
import com.ssy.lingxi.common.exception.BusinessException;
import com.ssy.lingxi.common.model.dto.UserLoginCacheDTO;
import com.ssy.lingxi.common.response.PageData;
import com.ssy.lingxi.common.response.ResponseCode;
import com.ssy.lingxi.common.response.Wrapper;
import com.ssy.lingxi.platform.manage.api.feign.inner.ShopControllerFeign;
import com.ssy.lingxi.platform.manage.api.model.dto.common.CommonIdListDTO;
import com.ssy.lingxi.platform.manage.api.model.vo.shop.ShopVO;
import com.ssy.lingxi.platform.template.service.constant.ActivityPageStatusEnum;
import com.ssy.lingxi.platform.template.service.constant.CommonBooleanEnum;
import com.ssy.lingxi.platform.template.service.entity.adorn.ActivityPage;
import com.ssy.lingxi.platform.template.service.entity.adorn.QActivityPage;
import com.ssy.lingxi.platform.template.service.model.dto.common.CommonStatusDTO;
import com.ssy.lingxi.platform.template.service.model.dto.common.IdDTO;
import com.ssy.lingxi.platform.template.service.model.dto.web.SaveActivityPageDTO;
import com.ssy.lingxi.platform.template.service.model.qo.ActivityPageQO;
import com.ssy.lingxi.platform.template.service.repository.ActivityPageRepository;
import com.ssy.lingxi.platform.template.service.service.web.ActivityPageWebService;
import com.ssy.lingxi.scheduler.api.feign.ScheduleTaskFeign;
import com.ssy.lingxi.scheduler.api.model.ScheduleTaskCallbackVO;
import com.ssy.lingxi.scheduler.api.model.ScheduleTaskIdVO;
import com.ssy.lingxi.scheduler.api.model.ScheduleTaskVO;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author fhj
 * @version 2.0.0
 * @menu web - 活动页 - 业务实现层
 * @date 2021/08/06
 */
@Slf4j
@Service
public class ActivityPageWebServiceImpl implements ActivityPageWebService {

    @Resource
    private JPAQueryFactory jpaQueryFactory;
    @Resource
    private ActivityPageRepository activityPageRepository;
    @Resource
    private ScheduleTaskFeign scheduleTaskFeign;
    @Resource
    private ShopControllerFeign shopControllerFeign;

    @Override
    @ApiOperation(value = "列表")
    public Wrapper<PageData<ActivityPage>> page(ActivityPageQO qo, UserLoginCacheDTO user) {
        //查询表实体 - 活动页
        QActivityPage activityPage = QActivityPage.activityPage;

        //查询条件构造 - 必要条件
        BooleanBuilder predicates = new BooleanBuilder();
        predicates.and(activityPage.memberId.eq(user.getMemberId()));
        predicates.and(activityPage.roleId.eq(user.getMemberRoleId()));
        predicates.and(activityPage.type.eq(qo.getType()));

        //搜索条件
        if (StringUtils.isNotBlank(qo.getName())) {
            predicates.and(activityPage.name.like("%" + qo.getName() + "%"));
        }
        if (qo.getStartTime() != null) {
            predicates.and(activityPage.startTime.goe(qo.getStartTime()));
        }
        if (qo.getEndTime() != null) {
            predicates.and(activityPage.endTime.loe(qo.getEndTime()));
        }
        if (qo.getEnvironment() != null) {
            predicates.and(activityPage.environment.eq(qo.getEnvironment()));
        }
        if (qo.getStatus() != null) {
            predicates.and(activityPage.status.eq(qo.getStatus()));
        }

        //查询
        JPAQuery<ActivityPage> query = jpaQueryFactory.select(
                Projections.bean(ActivityPage.class,
                        activityPage.id,
                        activityPage.memberId,
                        activityPage.roleId,
                        activityPage.type,
                        activityPage.name,
                        activityPage.templateId,
                        activityPage.templateName,
                        activityPage.templatePicUrl,
                        activityPage.environment,
                        activityPage.environmentName,
                        activityPage.shopId,
                        activityPage.shopName,
                        activityPage.startTime,
                        activityPage.endTime,
                        activityPage.status,
                        activityPage.statusName,
                        activityPage.createTime
                )
        ).from(activityPage).where(predicates);

        //总记录数
        long totalCount = query.fetchCount();

        //列表数据
        List<ActivityPage> list = query.orderBy(activityPage.createTime.desc()).offset((long) (qo.getCurrent() - 1) * qo.getPageSize()).limit(qo.getPageSize()).fetch();
        //国际化返回活动状态
        list.forEach(a -> a.setStatusName(ActivityPageStatusEnum.getMessage(a.getStatus())));
        //调用平台管理服务->获取商城子域名
        List<Long> shopIds = list.stream().map(ActivityPage::getShopId).collect(Collectors.toList());
        CommonIdListDTO dto = new CommonIdListDTO();
        dto.setIdList(shopIds);
        Wrapper<List<ShopVO>> wrapper = shopControllerFeign.byShopIds(dto);
        List<ShopVO> voList = wrapper.getData();
        if (!CollectionUtils.isEmpty(voList)) {
            Map<Long, String> urlMap = voList.stream().collect(Collectors.toMap(ShopVO::getId, ShopVO::getUrl));
            Map<Long, Integer> selfMap = voList.stream().collect(Collectors.toMap(ShopVO::getId, ShopVO::getSelf));
            list.forEach(p -> p.setUrl(urlMap.get(p.getShopId())));
            list.forEach(p->p.setSelf(selfMap.get(p.getShopId())));
        }
        return Wrapper.success(new PageData<>(totalCount, list));
    }

    @Override
    @ApiOperation(value = "详情")
    public Wrapper<ActivityPage> get(IdDTO dto) {
        ActivityPage activityPage = activityPageRepository.findById(dto.getId()).orElse(null);
        if (activityPage == null) {
            throw new BusinessException(ResponseCode.PT_RECORDS_DON_T_EXIST);
        }
        return Wrapper.success(activityPage);
    }

    @Override
    public Wrapper<ActivityPage> getDetail(IdDTO dto) {
        ActivityPage activityPage = activityPageRepository.findById(dto.getId()).orElse(null);
        if (activityPage == null) {
            throw new BusinessException(ResponseCode.PT_RECORDS_DON_T_EXIST);
        }
        if (activityPage.getStatus().equals(ActivityPageStatusEnum.WAIT_OPEN_LINE.getCode())
                || activityPage.getStatus().equals(ActivityPageStatusEnum.ALREADY_OPEN_LINE.getCode())) {
            return Wrapper.fail(ResponseCode.PT_THE_EVENT_HAS_NOT_STARTED_YET);
        }
        if (activityPage.getStatus().equals(ActivityPageStatusEnum.OFF_LINE.getCode())
                || activityPage.getStatus().equals(ActivityPageStatusEnum.END.getCode())) {
            return Wrapper.fail(ResponseCode.PT_THE_EVENT_IS_OVER);
        }
        return Wrapper.success(activityPage);
    }

    @Override
    @Transactional
    @ApiOperation(value = "新增")
    public Wrapper<Long> add(SaveActivityPageDTO dto, UserLoginCacheDTO user) {
        //校验
        if (dto.getStartTime() >= dto.getEndTime() || System.currentTimeMillis() >= dto.getEndTime()) {
            return Wrapper.fail(ResponseCode.PT_DEADLINE_MUST_BE_GREATER_THAN_STARTING_TIME_AND_GREATER_THAN_THE_CURRENT_TIME);
        }

        //新增
        ActivityPage activityPage = new ActivityPage();
        BeanUtils.copyProperties(dto, activityPage);
        activityPage.setMemberName(user.getCompany());
        activityPage.setMemberId(user.getMemberId());
        activityPage.setRoleId(user.getMemberRoleId());
        activityPage.setEnvironmentName(ShopEnvironmentEnum.getMessage(dto.getEnvironment()));
        activityPage.setStatusName(ActivityPageStatusEnum.getMessage(activityPage.getStatus()));
        activityPageRepository.saveAndFlush(activityPage);

        return Wrapper.success(activityPage.getId());
    }

    @Override
    @Transactional
    @ApiOperation(value = "修改")
    public Wrapper<Void> update(SaveActivityPageDTO dto, UserLoginCacheDTO user) {
        //获取活动页
        ActivityPage activityPage = activityPageRepository.findById(dto.getId()).orElse(null);

        //校验
        if (activityPage == null) {
            throw new BusinessException(ResponseCode.PT_RECORDS_DON_T_EXIST);
        }
        if (dto.getStartTime() >= dto.getEndTime() || System.currentTimeMillis() >= dto.getEndTime()) {
            return Wrapper.fail(ResponseCode.PT_DEADLINE_MUST_BE_GREATER_THAN_STARTING_TIME_AND_GREATER_THAN_THE_CURRENT_TIME);
        }
        if (!ActivityPageStatusEnum.isUpdate(activityPage.getStatus())) {
            return Wrapper.fail(ResponseCode.PT_CAN_ONLY_BE_MODIFIED);
        }

        //修改
        activityPage.setName(dto.getName());
        activityPage.setStartTime(dto.getStartTime());
        activityPage.setEndTime(dto.getEndTime());
        activityPageRepository.saveAndFlush(activityPage);

        return Wrapper.success();
    }

    @Override
    @Transactional
    @ApiOperation(value = "删除")
    public Wrapper<Void> delete(IdDTO dto, UserLoginCacheDTO user) {
        //获取活动页
        ActivityPage activityPage = activityPageRepository.findById(dto.getId()).orElse(null);

        //校验
        if (activityPage == null) {
            throw new BusinessException(ResponseCode.PT_RECORDS_DON_T_EXIST);
        }
        if (!ActivityPageStatusEnum.isDelete(activityPage.getStatus())) {
            return Wrapper.fail(ResponseCode.PT_CAN_ONLY_BE_DELETE);
        }

        //删除
        activityPageRepository.deleteById(dto.getId());

        return Wrapper.success();
    }

    @Override
    @Transactional
    @ApiOperation(value = "上下线")
    public Wrapper<Void> openOffLine(CommonStatusDTO dto, UserLoginCacheDTO user) {
        //获取活动页
        ActivityPage activityPage = activityPageRepository.findById(dto.getId()).orElse(null);

        //校验
        if (activityPage == null) {
            throw new BusinessException(ResponseCode.PT_RECORDS_DON_T_EXIST);
        }

        //开始活动页、结束活动页的回调接口地址
        final String startUrl = "/template/web/activityPage/start";
        final String endUrl = "/template/web/activityPage/end";

        //上线
        if (dto.getStatus().equals(CommonBooleanEnum.YES.getCode())) {
            //校验
            if (!ActivityPageStatusEnum.isOpenLine(activityPage.getStatus())) {
                return Wrapper.fail(ResponseCode.PT_ONLY_OFF_LINE);
            }
            if (CollectionUtil.isEmpty(activityPage.getAdornContent())) {
                return Wrapper.fail(ResponseCode.PT_FURNISH_FIRST_BEFORE_ON_LINE);
            }

            //活动页开始时间还没到 -> 活动页状态修改为已上线。创建定时任务，活动页的自动开启与自动结束。
            if (System.currentTimeMillis() < activityPage.getStartTime()) {
                activityPage.setStatus(ActivityPageStatusEnum.ALREADY_OPEN_LINE.getCode());
                activityPage.setStatusName(ActivityPageStatusEnum.ALREADY_OPEN_LINE.getMessage());

                //创建定时任务 -> 活动页开始
                activityPage.setStartTimeTaskId(
                        createScheduleTask(user, activityPage.getId(), activityPage.getStartTime(), startUrl)
                );

                //创建定时任务 -> 活动页结束
                activityPage.setEndTimeTaskId(
                        createScheduleTask(user, activityPage.getId(), activityPage.getEndTime(), endUrl)
                );
            }

            //活动页开始时间到了 -> 活动页状态改为进行中。创建定时任务，活动页的自动结束。
            if (System.currentTimeMillis() >= activityPage.getStartTime()) {
                activityPage.setStatus(ActivityPageStatusEnum.START.getCode());
                activityPage.setStatusName(ActivityPageStatusEnum.START.getMessage());

                //创建定时任务 -> 活动页结束
                activityPage.setEndTimeTaskId(
                        createScheduleTask(user, activityPage.getId(), activityPage.getEndTime(), endUrl)
                );
            }

            //活动页结束时间到了 -> 活动页改为已结束
            if (System.currentTimeMillis() > activityPage.getEndTime()) {
                activityPage.setStatus(ActivityPageStatusEnum.END.getCode());
                activityPage.setStatusName(ActivityPageStatusEnum.END.getMessage());
            }
        }

        //下线
        if (dto.getStatus().equals(CommonBooleanEnum.NO.getCode())) {
            //校验
            if (!ActivityPageStatusEnum.isOffLine(activityPage.getStatus())) {
                return Wrapper.fail(ResponseCode.PT_ONLY_OFF_LINE);
            }

            //活动页改为已下线
            activityPage.setStatus(ActivityPageStatusEnum.OFF_LINE.getCode());
            activityPage.setStatusName(ActivityPageStatusEnum.OFF_LINE.getMessage());

            //删除定时任务（开始时间的定时任务）
            deleteScheduleTask(activityPage.getStartTimeTaskId());

            //删除定时任务（结束时间的定时任务）
            deleteScheduleTask(activityPage.getEndTimeTaskId());
        }

        //更新活动页状态
        activityPageRepository.saveAndFlush(activityPage);

        return Wrapper.success();
    }

    @Override
    @Transactional
    @ApiOperation(value = "装修")
    public Wrapper<Void> adorn(SaveActivityPageDTO dto, UserLoginCacheDTO user) {
        //获取活动页
        ActivityPage activityPage = activityPageRepository.findById(dto.getId()).orElse(null);

        //校验
        if (activityPage == null) {
            throw new BusinessException(ResponseCode.PT_RECORDS_DON_T_EXIST);
        }

        //保存装修内容
        activityPage.setAdornContent(dto.getAdornContent());
        activityPageRepository.saveAndFlush(activityPage);

        return Wrapper.success();
    }

    @Override
    @ApiOperation(value = "定时服务回调 - 开始活动页")
    public Wrapper<Void> start(ScheduleTaskCallbackVO dto) {
        log.info("定时服务回调 - 开始活动页 - 参数:{}", JSONUtil.toJsonStr(dto));
        List<ActivityPage> list = activityPageRepository.findAllById(dto.getDataIds());
        if (CollectionUtil.isEmpty(list)) {
            return Wrapper.fail(ResponseCode.PT_CAN_T_GET_ACTIVE_PAGE_DATA);
        }

        //批量更新活动页状态为进行中, 只更新已上线的活动页
        list.forEach(a -> {
            if (a.getStatus().equals(ActivityPageStatusEnum.ALREADY_OPEN_LINE.getCode())) {
                a.setStatus(ActivityPageStatusEnum.START.getCode());
                a.setStatusName(ActivityPageStatusEnum.START.getMessage());
            }
        });
        activityPageRepository.saveAll(list);

        return Wrapper.success();
    }

    @Override
    @ApiOperation(value = "定时服务回调 - 结束活动页")
    public Wrapper<Void> end(ScheduleTaskCallbackVO dto) {
        log.info("定时服务回调 - 结束活动页 - 参数:{}", JSONUtil.toJsonStr(dto));
        List<ActivityPage> list = activityPageRepository.findAllById(dto.getDataIds());
        if (CollectionUtil.isEmpty(list)) {
            return Wrapper.fail(ResponseCode.PT_CAN_T_GET_ACTIVE_PAGE_DATA);
        }

        //批量更新活动页状态为已结束, 只更新进行中的活动页
        list.forEach(a -> {
            if (a.getStatus().equals(ActivityPageStatusEnum.START.getCode())) {
                a.setStatus(ActivityPageStatusEnum.END.getCode());
                a.setStatusName(ActivityPageStatusEnum.END.getMessage());
            }
        });
        activityPageRepository.saveAll(list);

        return Wrapper.success();
    }

    @Override
    @ApiOperation(value = "列表（装修）")
    public Wrapper<PageData<ActivityPage>> listAdorn(ActivityPageQO qo, UserLoginCacheDTO user) {
        //校验
        if (qo.getShopId() == null) {
            return Wrapper.fail(ResponseCode.PT_STORE_ID_CANNOT_BE_EMPTY);
        }

        //查询表实体 - 活动页
        QActivityPage activityPage = QActivityPage.activityPage;

        //查询条件构造 - 必要条件
        BooleanBuilder predicates = new BooleanBuilder();
        predicates.and(activityPage.type.eq(qo.getType()));
        predicates.and(activityPage.shopId.eq(qo.getShopId()));
        predicates.and(activityPage.status.in(ActivityPageStatusEnum.ALREADY_OPEN_LINE.getCode(), ActivityPageStatusEnum.START.getCode()));

        //搜索条件
        if (user.getLoginSource().equals(UserLoginSourceEnum.BUSINESS_WEB.getCode())) {
            predicates.and(activityPage.memberId.eq(user.getMemberId()));
            predicates.and(activityPage.roleId.eq(user.getMemberRoleId()));
        }
        if (StringUtils.isNotBlank(qo.getName())) {
            predicates.and(activityPage.name.like("%" + qo.getName() + "%"));
        }
        if (qo.getStartTime() != null) {
            predicates.and(activityPage.startTime.goe(qo.getStartTime()));
        }
        if (qo.getEndTime() != null) {
            predicates.and(activityPage.endTime.loe(qo.getEndTime()));
        }

        //查询
        JPAQuery<ActivityPage> query = jpaQueryFactory.select(
                Projections.bean(ActivityPage.class,
                        activityPage.id,
                        activityPage.memberId,
                        activityPage.roleId,
                        activityPage.memberName,
                        activityPage.type,
                        activityPage.name,
                        activityPage.templateId,
                        activityPage.templateName,
                        activityPage.templatePicUrl,
                        activityPage.environment,
                        activityPage.environmentName,
                        activityPage.shopId,
                        activityPage.shopName,
                        activityPage.startTime,
                        activityPage.endTime,
                        activityPage.status,
                        activityPage.statusName,
                        activityPage.createTime
                )
        ).from(activityPage).where(predicates);

        //总记录数
        long totalCount = query.fetchCount();

        //列表数据
        List<ActivityPage> list = query.orderBy(activityPage.createTime.desc()).offset((long) (qo.getCurrent() - 1) * qo.getPageSize()).limit(qo.getPageSize()).fetch();

        return Wrapper.success(new PageData<>(totalCount, list));
    }

    /**
     * 创建定时任务
     *
     * @param user        当前登录用户
     * @param dataId      活动页ID
     * @param execTime    执行时间
     * @param callbackUrl 回调接口地址
     */
    private Long createScheduleTask(UserLoginCacheDTO user, Long dataId, Long execTime, String callbackUrl) {
        //校验
        if (!ObjectUtil.isAllNotEmpty(user, dataId, execTime, callbackUrl)) {
            throw new BusinessException(ResponseCode.REQUEST_PARAMETER_VALIDATION_EXCEPTION);
        }

        //调用定时服务 -> 创建定时任务
        ScheduleTaskVO vo = new ScheduleTaskVO();
        vo.setServiceName("platform-template-service");
        vo.setCallbackUrl(callbackUrl);
        vo.setMemberId(user.getMemberId());
        vo.setRoleId(user.getMemberRoleId());
        vo.setDataIds(Stream.of(dataId).collect(Collectors.toList()));
        vo.setExecTime(execTime);
        Wrapper<Long> wrapper = scheduleTaskFeign.createScheduleTask(vo);
        if (wrapper.getCode() != ResponseCode.SUCCESS.getCode()) {
            log.error("调用定时服务失败:{}", wrapper.getMessage());
            throw new BusinessException(ResponseCode.SERVICE_SCHEDULER_ERROR);
        }

        return wrapper.getData();
    }

    /**
     * 删除定时任务
     *
     * @param taskId 定时任务ID
     */
    private void deleteScheduleTask(Long taskId) {
        //调用定时服务 -> 删除任务
        if (taskId != null) {
            ScheduleTaskIdVO vo = new ScheduleTaskIdVO();
            vo.setTaskId(taskId);
            Wrapper<Void> wrapper = scheduleTaskFeign.deleteScheduleTask(vo);
            if (wrapper.getCode() != ResponseCode.SUCCESS.getCode()) {
                log.error("调用定时服务失败:{}", wrapper.getMessage());
                throw new BusinessException(ResponseCode.SERVICE_SCHEDULER_ERROR);
            }
        }
    }
}
